package com.lemon.boot.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lemon.boot.common.base.BaseServiceImpl;
import com.lemon.boot.common.consts.ApiConst;
import com.lemon.boot.common.consts.RedisKeyConst;
import com.lemon.boot.common.entity.LoginUserDetail;
import com.lemon.boot.common.enums.DeleteEnum;
import com.lemon.boot.common.enums.RoleKeyEnum;
import com.lemon.boot.common.enums.StatusEnum;
import com.lemon.boot.common.exception.CustomException;
import com.lemon.boot.common.service.RedisService;
import com.lemon.boot.common.utils.*;
import com.lemon.boot.common.web.Result;
import com.lemon.boot.dao.UserDAO;
import com.lemon.boot.dto.InviteDTO;
import com.lemon.boot.dto.UserDTO;
import com.lemon.boot.po.*;
import com.lemon.boot.service.*;
import lombok.extern.log4j.Log4j2;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户表 业务实现类
 *
 * @author 李猛
 * @since 2024-02-11
 */
@Log4j2
@Service
public class UserServiceImpl extends BaseServiceImpl<UserDAO, UserPO, UserDTO> implements IUserService {
    @Resource
    private IRoleService iRoleService;
    @Resource
    private IMenuResourceService iMenuResourceService;
    @Resource
    private IDeptService iDeptService;
    @Resource
    private IDeptUserService iDeptUserService;
    @Resource
    private IUserRoleService iUserRoleService;
    @Resource
    private RedisService redisService;
    @Resource
    @Lazy
    private PasswordEncoder passwordEncoder;

    @Override
    @Transactional
    public String add(UserPO userPO) {
        return this.addOrEdit(userPO);
    }

    @Override
    @Transactional
    public String edit(UserPO userPO) {
        return this.addOrEdit(userPO);
    }

    @Override
    @Transactional
    public String addOrEdit(UserPO userPO) {
        //1.判断是新增还是修改
        boolean isAdd = userPO.getId() == null;

        //2.判断用户名是否重复
        boolean exist = this.query()
                .select("1")
                .eq("username", userPO.getUsername())
                .ne(!isAdd, "id", userPO.getId())
                .last("limit 1")
                .exists();
        if (exist) {
            return String.format("用户名\"%s\"已存在！", userPO.getUsername());
        }

        //3.当前用户
        LoginUserDetail currentUser = JwtUtil.getCurrentUser();

        //4.添加时，设置默认值
        if (isAdd) {//4.1.新增时设置默认值
            userPO.setTenantId(currentUser.getTenantId());
        } else {//4.2.修改时，需要删除中间表
            //4.3.删除部门用户中间表
            iDeptUserService.update().eq("user_id", userPO.getId()).remove();
            //4.4.删除用户角色中间表
            iUserRoleService.update().eq("user_id", userPO.getId()).remove();
        }

        //5.执行添加或修改
        String result = super.addOrEdit(userPO);

        //6.新增角色
        if (!CollectionUtils.isEmpty(userPO.getRoleIdList())) {
            List<UserRolePO> userRoleList = userPO.getRoleIdList().stream().map(item -> new UserRolePO().setRoleId(item).setUserId(userPO.getId())).collect(Collectors.toList());
            iUserRoleService.saveBatch(userRoleList);
        }

        //7.新增部门
        if (!CollectionUtils.isEmpty(userPO.getDeptIdList())) {
            //7.1.查询部门 id,tree_link_id
            List<DeptPO> deptList = iDeptService.query().select("id", "name", "tree_link_id")
                    .in("id", userPO.getDeptIdList())
                    .eq(StatusEnum.ENABLE.getColumn(), StatusEnum.ENABLE.getStatus())
                    .eq(DeleteEnum.UNABLE.getColumn(), DeleteEnum.UNABLE.getDeleted())
                    .list();

            //7.2.根据 tree_link_id 判断父子关系
            List<Long> deptIdList = new ArrayList<>();
            for (DeptPO deptPO : deptList) {
                boolean match = deptList.stream().anyMatch(item -> !deptPO.getTreeLinkId().equals(item.getTreeLinkId()) && deptPO.getTreeLinkId().contains(item.getTreeLinkId()));
                if (match) {
                    deptIdList.add(deptPO.getId());
                }
            }

            //7.3.去掉子部门
            List<DeptUserPO> deptUserList = userPO.getDeptIdList().stream().filter(item -> !deptIdList.contains(item))
                    .map(item -> new DeptUserPO().setUserId(userPO.getId()).setDeptId(item)).collect(Collectors.toList());

            //7.4.批量添加
            iDeptUserService.saveBatch(deptUserList);
        }
        return result;
    }

    @Override
    public Map<String, Object> getByCurrentId(Long id) {
        return this.baseMapper.getByCurrentId(id);
    }

    @Override
    public LoginUserDetail getByUsername(String username) {
        //1.查询用户信息
        Map<String, Object> param = ApiConst.enableStatusAndUnableDelete();
        param.put("username", username);
        List<LoginUserDetail> loginUserDetailList = this.baseMapper.getByUsername(param);
        if (CollectionUtils.isEmpty(loginUserDetailList) || loginUserDetailList.size() != 1) {
            throw new CustomException("用户名错误或租户信息异常！");
        }
        LoginUserDetail loginUserDetail = loginUserDetailList.get(0);

        //2.获取对应的角色
        List<RolePO> roleList = iRoleService.getByUserId(loginUserDetail.getUserId(), loginUserDetail.getTenantId());
        if (CollectionUtils.isEmpty(roleList)) {
            throw new CustomException("角色信息异常！");
        }

        //3.角色ID集合
        List<Long> roleIdList = roleList.stream().map(RolePO::getId).collect(Collectors.toList());
        loginUserDetail.setRoleIdList(roleIdList);

        //4.角色授权
        List<GrantedAuthority> grantedAuthorities = roleList.stream()
                .map(RolePO::getRoleKey)
                .distinct()
                .map(item -> String.format("%s%s", SecurityUtil.ROLE_PREFIX, item.toUpperCase(Locale.ROOT)))
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());

        //5.查询权限
        List<String> resourceList = iMenuResourceService.getByUserId(loginUserDetail.getUserId(), loginUserDetail.getTenantId(), roleIdList);
        if (!CollectionUtils.isEmpty(resourceList)) {
            //5.设置接口授权
            List<GrantedAuthority> resourceGrantedAuthorities = resourceList
                    .stream()
                    .distinct()
                    .map(item -> String.format("%s%s", SecurityUtil.PERMISSION_PREFIX, item.replaceAll("/", ":")))
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());
            grantedAuthorities.addAll(resourceGrantedAuthorities);
        }

        loginUserDetail.setAuthorities(grantedAuthorities);

        //6.修改用户的IP和登录时间
        this.baseMapper.updateIpAndLoginTime(RequestUtil.getIp(), loginUserDetail.getUserId());

        return loginUserDetail;
    }

    @Override
    public UserPO get(Long id) {
        return this.baseMapper.get(id);
    }

    @Override
    public Page<UserPO> page(UserDTO userDTO) {
        //1.当前用户
        final LoginUserDetail currentUser = JwtUtil.getCurrentUser();
        //3.判断用户角色
        if (!SecurityUtil.hasRole(currentUser, RoleKeyEnum.SUPER)) {
            if (SecurityUtil.hasRole(currentUser, RoleKeyEnum.ADMIN)) {
                userDTO.setTenantId(currentUser.getTenantId());
            } else {
                if (SecurityUtil.hasRole(currentUser, RoleKeyEnum.COMMON)) {
                    List<Long> userIdList = iDeptUserService.getChildUserIdByUserId(currentUser.getUserId());
                    userDTO.setUserIdList(userIdList);
                } else {
                    throw new CustomException("用户角色信息异常");
                }
            }
        }
        return this.baseMapper.getPage(userDTO);
    }

    @Override
    public List<UserPO> getByDeptId(Long deptId) {
        return this.baseMapper.getByDeptId(deptId);
    }

    @Override
    @Transactional
    public UserPO register(UserDTO userDTO) {
        //1.判断输入的值是否正确
        Result<String> result = ObjectFieldUtil.isNull(userDTO, "name", "phone", "sex", "email", "provinceId", "cityId", "countyId", "address");
        if (!result.isSuccess()) {
            throw new CustomException(result.getMessage());
        }

        if (!StringUtils.hasText(userDTO.getUsername()) || userDTO.getUsername().length() <= 5) {
            throw new CustomException(String.format("用户名\"%s\"过短！", userDTO.getUsername()));
        }
        String password = RsaUtil.getInstance().decryptByPrivateKey(userDTO.getPassword());
        if (!StringUtils.hasText(userDTO.getPassword()) || password.length() <= 5) {
            throw new CustomException("密码过短！");
        }

        //2.判断邀请码是否存在
        String keyCode = String.format("%s%s", RedisKeyConst.REDIS_KEY_LOGIN_CAPTCHA_CODE, userDTO.getCode());
        boolean hasCode = redisService.has(keyCode);
        if (!hasCode) {
            throw new CustomException("邀请码不存在，请重新输入！");
        }

        //3.获取邀请人信息
        String key = String.format("%s%s", RedisKeyConst.REDIS_KEY_EMAIL_INVITE_USER, userDTO.getEmail());
        InviteDTO inviteDTO = redisService.get(key, InviteDTO.class);

        //4.判断用户名是否存在
        boolean exist = this.query().select("1").eq("username", userDTO.getUsername()).last("limit 1").exists();
        if (exist) {
            throw new CustomException("用户名重复，请重新输入！");
        }

        //3.设置默认值，并添加
        UserPO userPO = ConverterUtil.toBean(userDTO, UserPO.class);
        userPO.setPassword(passwordEncoder.encode(password));
        userPO.setTenantId(inviteDTO.getTenantId());
        userPO.setStatus(StatusEnum.ENABLE.getStatus());
        userPO.setLastLoginTime(LocalDateUtil.toString(LocalDateTime.now()));
        this.save(userPO);
        return userPO;
    }

    @Override
    public String updateByCurrent(UserPO userPO) {
        //1.判断是否是当前用户
        if (!Objects.equals(userPO.getId(), JwtUtil.getCurrentUser().getUserId())) {
            return "非本人禁止修改！";
        }

        //2.判断用户名是否重复
        boolean exists = this.query().select("1")
                .eq("username", userPO.getUsername())
                .ne("id", userPO.getId())
                .last("limit 1")
                .exists();
        if (exists) {
            return String.format("用户名\"%s\"已存在！", userPO.getUsername());
        }

        //3.设置不修改字段
        userPO.setUpdateBy(userPO.getName());
        userPO.setUpdateTime(LocalDateTime.now());
        return super.edit(ObjectFieldUtil.setNull(userPO, "tenantId", "password", "lastLoginIp", "lastLoginTime", "createBy", "createTime", "updateBy", "status", "deleted"));
    }

    @Override
    @Transactional
    public UserPO register(UserPO userPO) {
        //1.判断信息是否为空
        Result<String> result = ObjectFieldUtil.isNull(userPO, "username", "name", "sex");
        if (!result.isSuccess()) {
            throw new CustomException(result.getMessage());
        }

        //2.判断用户名是否存在
        boolean userExist = this.query().select("1")
                .eq("username", userPO.getUsername()).last("limit 1").exists();
        if (userExist) {
            throw new CustomException("用户名重复，请重新输入！");
        }

        if (userPO.getUsername().length() <= 4) {
            throw new CustomException(String.format("用户名\"%s\"过短！", userPO.getUsername()));
        }
        String password = RsaUtil.getInstance().decryptByPrivateKey(userPO.getPassword());
        if (!StringUtils.hasText(userPO.getPassword()) || password.length() <= 4) {
            throw new CustomException("密码过短！");
        }
        if (!ValidateUtil.isPhone(userPO.getPhone())) {
            throw new CustomException("请输入正确的手机号！");
        }
        if (!ValidateUtil.isEmail(userPO.getEmail())) {
            throw new CustomException("请输入正确的邮箱！");
        }

        //3.设置用户默认值，并添加用户
        userPO.setPassword(passwordEncoder.encode(password));
        this.save(userPO);
        return userPO;
    }
}